<template>
  <div class="demo-book">
    <div ref="demo" class="demo-box" v-if="type === 'vue'"></div>
    <pre v-show="isCode || type !== 'vue'" :style="{
      borderTop: type === 'vue' ? '1px solid var(--border-primary-color-5)' : 'none'
    }">
      <code ref="codeDom" :class="`language-${type}`" v-html="formatCode(code, type)"></code>
    </pre>
    <div class="demo-book-option" v-if="type === 'vue'">
      <span @click="isCode = !isCode">{{
        isCode ? "隐藏代码" : "展开代码"
      }}</span>
    </div>
  </div>
</template>

<script lang="ts">
import hljs from "highlight.js";
import cssToObject from "css-to-object";
import { defineComponent, onMounted, Ref, ref } from "vue";
import createApp from "../../create-app";
import packages from "../../package";
import { Tools } from "~/index";
/** 判断代码内是否包含 import 语法 */
const idImport = (str: string) => /import/g.test(str);
/** 解析 import 并把 import 内容加入全局对象 */
const setGlobalContext = (str: string) => {
  const _str_: string = str
    .replace(/import/g, "")
    .replace(/from/g, "")
    .replace(/\s/g, "")
    .replace(/"/g, "'")
    .replace(/;/g, "");
  /** 导入的变量名 */
  const name = _str_.substring(0, _str_.indexOf("'"));
  /** 路径 */
  const path = _str_.replace(name, "").replace(/'/g, "");
  /** 有变量名，需要加入全局对象 */
  if (/from/g.test(str)) {
    /** 判断是否按需导入 */
    if (/(\{|\})/g.test(str)) {
      /** 没有导入任何内容 */
      if (name.replace("{", "").replace("}", "") === "") {
        console.warn(`在 ${path} 中按需导入的内容为空：${str}`);
      } else {
        const list = name.replace("{", "").replace("}", "").split(",");
        list.forEach((item) => {
          if ((window as any)[item] === undefined) {
            (window as any)[item] = (packages as any)[path][item];
          }
        });
      }
    } /** 判断全局 */ else {
      if ((packages as any)[path]) {
        if ((window as any)[name] === undefined) {
          /** 将导入规则加入全局变量 */
          (window as any)[name] = (packages as any)[path];
        }
      } else {
        console.error(`package 队列内没有 "${path}" 这个包`);
      }
    }
  } /** 直接导入 */ else {
    console.warn(
      `在 markdown 代码贴中导入了 "${path}" 这个包，但是没有声明变量，无法直接注入到实例中`
    );
  }
};
export default defineComponent({
  name: "cb-demo-book",
  props: {
    type: {
      type: String,
    },
    code: {
      type: String,
    },
  },
  setup({ type, code }: any) {
    /** 渲染 Demo 的 Dom */
    const demo: Ref<any> = ref(null);
    /** 是否展示代码 */
    const isCode: Ref<boolean> = ref(false);
    /** 代码贴渲染 */
    const codeDom: Ref<any> = ref(null);
    /**  随机数字符串 */
    const random = Tools.String.random(8);
    /** 格式化整理代码贴 */
    const formatCode = (str: string, type: string): string => {
      let _code_: string = "";
      /** 存储每一行代码 */
      let codes = str.split("\n");
      if (codes[codes.length - 1] === "") {
        codes.pop();
      }
      for (let i = 0; i < codes.length; i++) {
        _code_ += `${codes[i]}\n`;
        continue;
      }
      return hljs.highlight(_code_, { language: type === 'vue' ? "xml": type }).value;
    };
    /** 渲染 Dom */
    const renderDom = () => {
      if (type === "vue") {
        /** 创建Dom转存模版 */
        const demoDom = document.createElement("div");
        /** 给 Dom 赋值 */
        demoDom.innerHTML = code;
        
        /** 存储执行代码 */
        let _code_ = "";

        if (demoDom.getElementsByTagName("script").length > 0) {
          /** 获取 script 标签 */
          const script = demoDom.getElementsByTagName("script")[0];
          /** 存储每一行代码 */
          const codes = script.innerHTML
            .split("\n")
            .filter((item) => item !== "");
          for (let i = 0; i < codes.length; i++) {
            if (idImport(codes[i])) {
              setGlobalContext(codes[i]);
            } else {
              _code_ += `${codes[i]}\n`;
            }
            continue;
          }
        }
        
        /** 获取组件内的样式表 */
        const style = demoDom.getElementsByTagName("style");
        /** 渲染样式 */
        if (style.length > 0) {
          /** 存储css */
          let styleArr = [];
          for (let i = 0; i < style.length; i++) {
            styleArr.push({
              module:
                style[i].getAttribute("scoped") === null ? "default" : "scoped",
              data:
                style[i].getAttribute("scoped") === null
                  ? style[i].innerHTML
                  : cssToObject(style[i].innerHTML),
            });
          }
          /** 判断是否有模块化的 css */
          const cssStatus =
            styleArr.filter((item) => item.module === "scoped").length > 0;
          /** 存储随机字符串 */
          if (cssStatus) {
            const html = demoDom.getElementsByTagName("template")[0].innerHTML;
            const div = document.createElement("div");
            div.innerHTML = html;
            const element = div.querySelectorAll("*");
            for (let i = 0; i < element.length; i++) {
              (element[i] as any).setAttribute(`data-v-${random}`, "");
            }
            demoDom.getElementsByTagName("template")[0].innerHTML =
              div.innerHTML;
          }
          /** css 对象 */
          let css: any = "";
          /** 循环 css */
          styleArr.forEach((item) => {
            /** 模块化的 css */
            if (item.module === "scoped") {
              let modelCss = "";
              for (let i in item.data) {
                let name = `${i}[data-v-${random}]`;
                let content = "";
                for (let j in item.data[i]) {
                  content += `${j}: ${item.data[i][j]};`;
                }
                modelCss += `${name} {${content}}`;
              }
              css += modelCss;
            } /** 没有模块化的 css */ else {
              css += item.data;
            }
          });
          const styleLabel = document.createElement('style');
          styleLabel.innerHTML = css;
          styleLabel.setAttribute('id', `${random}-style`);
          document.body.appendChild(styleLabel);
        }
        /** 判断 demo 的示例存在 */
        if (demoDom.getElementsByTagName("script").length > 0) {
          if (
            demoDom.getElementsByTagName("script")[0].innerHTML
            .replace(/\s/g, '')
            .replace(/\n/g, '')
            .replace(/^export default /, "")
            .replace(/(;)\n$/, "") !== ''
          ) {
            /** 动态创建节点渲染 */
            createApp({
              template: demoDom.getElementsByTagName("template")[0].innerHTML,
              ...eval(
                `(${_code_
                  .replace(/^export default /, "")
                  .replace(/(;)\n$/, "")})`
              ),
            }).mount(demo.value);
          } else {
            /** 动态挂在节点渲染模版 */
            createApp({
              template: demoDom.getElementsByTagName("template")[0].innerHTML,
            }).mount(demo.value);
          }
        } else {
          /** 动态挂在节点渲染模版 */
          createApp({
            template: demoDom.getElementsByTagName("template")[0].innerHTML,
          }).mount(demo.value);
        }
      }
    };
    onMounted(() => {
      renderDom();
    });
    return {
      demo,
      code,
      type,
      formatCode,
      isCode,
      codeDom,
    };
  },
});
</script>

<style lang="scss">
.hljs {
  color: var(--text-primary-color-7);
}
.hljs-comment,
.hljs-quote {
  color: var(--text-primary-color-2);
}
.hljs-doctag,
.hljs-formula,
.hljs-keyword {
  color: #c678dd;
}
.hljs-deletion,
.hljs-name,
.hljs-section,
.hljs-selector-tag,
.hljs-subst {
  color: var(--danger-color-9);
}
.hljs-literal {
  color: var(--success-color-3);
}
.hljs-addition,
.hljs-attribute,
.hljs-meta .hljs-string,
.hljs-regexp,
.hljs-string {
  color: var(--success-color-8);
}
.hljs-attr,
.hljs-number,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-pseudo,
.hljs-template-variable,
.hljs-type,
.hljs-variable {
  color: var(--warning-color);
}
.hljs-bullet,
.hljs-link,
.hljs-meta,
.hljs-selector-id,
.hljs-symbol,
.hljs-title {
  color: var(--primary-color-8);
}
.hljs-built_in,
.hljs-class .hljs-title,
.hljs-title.class_ {
  color: var(--warning-color-5);
}
</style>

<style lang="scss" scoped>
.demo-book {
  border: 1px solid var(--border-primary-color-5);
  border-radius: 4px;
  overflow: hidden;
  margin-bottom: 10px;
  .demo-box {
    padding: 15px;
  }
  pre {
    background: var(--border-primary-color-2);
    display: block;
    font-size: 0;
    line-height: 0;
    > [class^="language-"] {
      padding: 15px;
      display: block;
      font-size: 12px;
      line-height: 1.68;
      overflow: auto;
      color: var(--text-primary-color-7);
      
    }
  }
  .demo-book-option {
    border-top: 1px solid var(--border-primary-color-5);
    text-align: center;
    padding: 8px 0;
    span {
      font-size: 14px;
      line-height: 1.56;
      cursor: pointer;
      color: var(--primary-color-7);
    }
    span:hover {
      color: var(--primary-color);
    }
  }
}
</style>